home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-20 / nos_kit3.zip / TNC_TNC2.ZIP / TNC2KISS.MAC < prev    next >
Text File  |  1987-05-26  |  59KB  |  2,079 lines

  1. ;
  2. ;             KISS TNC for the TNC-2 and clones
  3. ;
  4. ; k3mc 30 Sep 86 - original version
  5. ;
  6. ; 1 Mar 87.  Fixed all known bugs.  Re-arrange code to allow ROMing (this
  7. ; means that data areas need to be initialized from the code).  Figure out the
  8. ; Stack Pointer given the amount of available RAM.  Include the codes 05 00
  9. ; and 05 01 to mean full duplex off and full duplex on, respectively.
  10. ; Clear out all available RAM.  Do a "dance" with LEDs when initially booted:
  11. ; Flash the LED(s) for about 5 seconds such that CON only flashes if you have
  12. ; 8k RAM, STA only flashes if 16k RAM, and STA and CON flash if 32k RAM.
  13. ;
  14. ; 29 Mar 87. Add code to discard BREAK chars, and chars with framing errors.
  15. ; Fix bug in ib_rca which did not discard null received frames.
  16.  
  17. FALSE    equ    0
  18. TRUE    equ    NOT FALSE
  19.  
  20.     .z80
  21.     aseg
  22.     org    100h        ;silly stuff for CP/M...
  23.  
  24.  
  25. ;ROM    equ    TRUE        ;uncomment this line to get ROM code 
  26.  
  27. ROM    equ    FALSE        ;uncomment this line for downloadable code
  28.  
  29.  
  30. ;Note: Next two equates don't matter unless ROM is True.
  31.  
  32. HOWIE    equ    FALSE        ;uncomment for ROM version org at 0
  33.  
  34. ;HOWIE    equ    TRUE        ;uncomment for org at 4800h for inclusion in
  35.                 ;Howie's code.
  36.  
  37.     if    ROM
  38.         if    HOWIE
  39.             .phase    4800h
  40. I_Register        equ    48h
  41.         else
  42.             .phase    0
  43. I_Register        equ    0
  44.         endif;    HOWIE
  45.     else
  46.             .phase    8000h
  47. I_Register        equ    80h
  48.     endif;    ROM
  49.  
  50.  
  51. SIO    equ    0dch        ;actually, only A5 is used for SIO -cs
  52.  
  53. A_dat    equ    SIO+0        ;Modem port
  54. A_ctl    equ    SIO+1        ;Modem port
  55.  
  56. B_dat    equ    SIO+2        ;user serial port
  57. B_ctl    equ    SIO+3        ;user serial port
  58.  
  59. DCD    equ    8        ;Bit in RR0, used in Ch A
  60.  
  61. TBE    equ    4        ;TX Buffer Empty bit
  62. RTS    equ    2        ;Request To Send (PTT bit in WR5 of Chan A)
  63. Framing_Error    equ    40h    ;Bit in RR1 for async framing error
  64. Break_Abort    equ    80h    ;Bit in RR0 for async Break detection
  65.  
  66. FEND    equ    300o        ;300 octal
  67. FESC    equ    333o        ;333 octal
  68. TFEND    equ    334o        ;334 octal
  69. TFESC    equ    335o        ;335 octal
  70.  
  71. ALEDon    equ    69h        ;bits for WR5 to turn on  STA LED
  72. ALEDoff    equ    0e9h        ;bits for WR5 to turn off STA LED
  73.  
  74. ALED    equ    80h        ;The DTR Bit in Ch A WR5, we will soon remove
  75.                 ;previous 2 definitions & use the memory loc.
  76.                 ;A_WR5 to hold Ch A WR5's value, because we
  77.                 ;need to be aware when we are transmitting!
  78.  
  79. BLEDon    equ    6ah        ;bits for WR5 to turn on  CON LED
  80. BLEDoff    equ    0eah        ;bits for WR5 to turn off CON LED
  81. BLED    equ    80h
  82.  
  83. N_events    equ    3    ;so far, only 3 real-time events
  84.                 ; 1 test event left untouched
  85.  
  86. start:
  87.     jp    code_start    ;go around this data area
  88. version:
  89.     defb    "v.32 26Mar 87"    ;13 bytes (exactly!) here for version string
  90.  
  91.     defw    ib_tbe        ;ch B transmitter buffer empty interrupt/user
  92.     defw    ib_ext        ;ch B ext/status change/user
  93.     defw    ib_rca        ;ch B received char available/user
  94.     defw    ib_special    ;ch B special receive condition/user
  95.  
  96.     defw    ia_tbe        ;ch A transmitter buffer empty interrupt/modem
  97.     defw    ia_ext        ;ch A ext/status change/modem
  98.     defw    ia_rca        ;ch A received char available/modem
  99.     defw    ia_special    ;ch A special receive condition/modem
  100.  
  101. a_init:
  102.     defb    18h,4,20h,1,1bh,7,7eh,5,0e9h,3,0c9h    ;For Modem
  103. a_size    equ    $-a_init
  104.  
  105. b_init:
  106.     defb    18h,4,44h,2,10h,3,0c1h,5,0eah,1,1fh    ;For TTY
  107. b_size    equ    $-b_init
  108.  
  109.  
  110. ;This is the data area which gets blasted into RAM upon startup:
  111. data_init:
  112.  
  113. ;nbuffers:
  114.         db    0        ;up to 255 buffers
  115. ;free:
  116.         dw    0        ;address of 1st buffer on free list
  117.  
  118. ;RX_buf:
  119.         dw    0        ;address of current Receive buffer
  120. ;RX_head:
  121.         dw    0        ;address of 1st RX buffer
  122. ;RX_Allocated_Buffer:
  123.         db    0        ;set non-zero if we're in RX state
  124.  
  125. ;RX_Flushing:
  126.         db    0        ;is non-0 if we ran out of buffer
  127.                     ;space and are currently flushing this
  128.                     ;frame being received.  Used by
  129.                     ;ia_rca and reset by ia_ext.
  130.  
  131.  
  132. ;In_Buffer:
  133.         dw    0        ;addr of current Input buffer
  134. ;In_Head:
  135.         dw    0        ;addr of 1st Input Buffer
  136. ;In_Allocated_Buffer:
  137.         db    0        ;is not 0 if we've already alloc'd buf
  138.  
  139. ;In_State:
  140.         db    1        ;convert back to 1 in v.32 code
  141.  
  142.                     ;input state machine state
  143.                     ;4 Mar 8: Make it 0 (from 1) becuz
  144.                     ;noise on line is first triggering the
  145.                     ;code to assume that a frame is coming
  146.                     ;from the host.....  Comment below was
  147.                     ;appropriate before
  148.                     ;assume that we've seen an FEND from
  149.                     ;(non-existent) "previous" frame. This
  150.                     ;means that when we are receiving data
  151.                     ;from user, there need be ONLY the
  152.                     ;FEND char at the end of a frame, and
  153.                     ;not at the beginning (although if a
  154.                     ;FEND is at the beginning, it is 
  155.                     ;ignored.)
  156.  
  157. ;Out_Started:
  158.         db    0        ;Output not started yet (Logical var)
  159. ;Out_Head_CBuf:
  160.         dw    out_top        ;address of buffer to be output rs232
  161. ;Out_Tail_CBuf:
  162.         dw    out_top        ;pointer to next free output buffer
  163. ;Out_Chain_Head:
  164.         dw    0        ;addr of buffer we are now outputting
  165.  
  166. ;TX_Started:
  167.         db    0        ;non-zero if we've begun TXing chars
  168. ;TX_Head_CBuf:
  169.         dw    TX_Top        ;Current active CBuf entry (if active)
  170. ;TX_Tail_CBuf:
  171.         dw    TX_Top        ;next free CBuf entry
  172.  
  173. ;TX_Chain_Head:
  174.         dw    0        ;holds address of the current buffer
  175.                     ;chain head that we are transmitting
  176. ;TX_Outstanding:
  177.         db    0        ;Number of TX CBufs queued up for TX
  178.  
  179. ;DCD_State:
  180.         db    0        ;is non 0 if DCD LED is on
  181.  
  182. ;these next two are used by the IB_TBE interrupt routine.
  183. ;ib_esc_mode:
  184.         db    0        ; not in escaped mode 
  185. ;ib_char:
  186.         ds    1        ; next char to send if escaped mode
  187. ;in_break:
  188.         db    0        ; non-zero if we are in a break detect
  189.                     ; sequence on the async port
  190. ;Full_Duplex:
  191.         db    0        ;not Full Duplex to start
  192. ;A_WR5:
  193.         db    ALEDoff        ;state of STA LED & RTS (PTT) line,
  194.                     ;mainly... (For Ch A only [modem] )
  195. ;B_WR5:
  196.         db    BLEDoff
  197.  
  198. data_size    equ    $-data_init
  199.  
  200. ;***************************************************************************
  201. code_start:
  202.     di                ;No interrupts for the moment...
  203.  
  204. ;Init SIO.  This is required even if we wanna flash LEDs...
  205.  
  206.     in    a,(A_ctl)        ;assure we are talking to ch 0
  207.     ld    c,A_ctl
  208.     ld    b,a_size
  209.     ld    hl,a_init
  210.     otir                ;init sync (modem) port
  211.  
  212. ;Init Async port, also to allow flashing LEDs
  213.  
  214.     in    a,(B_ctl)        ;assure we are talking to ch 0
  215.     ld    c,B_ctl
  216.     ld    b,b_size
  217.     ld    hl,b_init
  218.     otir                ;init async port & interrupt vector
  219.  
  220. ;
  221. ; Figure out where top of stack is, set stack pointer.
  222. ; silly TNC-2 does not do complete address decoding for the RAMs if you are
  223. ; using only the two 8k x 8 chips.  Hack to figure out top of memory so we can
  224. ; set stack pointer. Newer hack to see if we've only got 8k RAM.
  225.  
  226.     ld    a,(9fffh)    ;top of RAM if only 8K
  227.     cpl
  228.     ld    b,a
  229.     ld    (9fffh),a    ;write one's complement into mem
  230.  
  231.     ld    a,(9fffh)    
  232.     cp    b        ;see if it "took"
  233.     jp    z,ok_8        ;Yes, we have at least 8k of RAM
  234.  
  235.  
  236.     halt            ;else there is no RAM, so stop
  237.  
  238. ok_8:
  239.     ld    a,(0bfffh)    ;Top of RAM if 16K
  240.     cpl
  241.     ld    b,a
  242.     ld    (0bfffh),a    ;same one's complement hack
  243.  
  244.     ld    a,(0bfffh)
  245.     cp    b
  246.     jp    z,ok_16        ;we have at least 16k of RAM
  247.  
  248.     ld    sp,0a000h
  249.     ld    d,0ffh        ;blink CON LED
  250.     ld    e,0        ;but not STA LED  (i.e., we have 8k)
  251.     jp    stack_loaded    ;else we only have 8k of RAM
  252.                 ;because previous compare failed
  253.  
  254. ;Here if we've got at least 16K RAM
  255. ok_16:
  256.     ld    a,55h        ;one value
  257.     ld    (0bfffh),a
  258.     ld    a,0aah
  259.     ld    (0ffffh),a    ;other value
  260.  
  261.     ld    a,(0bfffh)    ;get what should be 55h if 32k
  262.  
  263.     cp    55h        
  264.     ld    sp,0
  265.     ld    de,0ffffh    ;blink both CON and STA LEDs (if 32k)
  266.     jr    z,stack_loaded    ;if is 55h, then we've got 32 K, else 16 k
  267.  
  268.     ld    sp,0c000h    ;force stack value.
  269.     ld    d,0        ;do not blink CON LED if 16k RAM
  270.  
  271. stack_loaded:
  272.     push    de        ;DE has logical values which tell us which
  273.                 ;LEDs to flash (which we do later...)
  274.     exx
  275.     pop    hl        ;temp. store this info in other reg set
  276.     exx
  277.  
  278.  
  279. ;Clear out RAM.
  280.  
  281.     ld    hl,0
  282.     add    hl,sp        ;now HL has value of SP (That is, Top of
  283.                 ;Memory + 1)
  284.     dec    hl        ;Now HL has Top of Memory address
  285.     ld    de,free_RAM    ;get start of available free RAM
  286.     xor    a        ;clear carry and set A to 0
  287.     ld    (de),a        ;first free RAM location is zeroed....
  288.     sbc    hl,de        ;get into HL # of bytes of free RAM.  If we 
  289.                 ;are in ROM, then all RAM is free, else if we
  290.                 ;are running from RAM, the code part is not
  291.                 ;free, and this compensates for this.
  292.  
  293.     dec    hl        ;one fewer bytes for number to move...
  294.     ld    b,h
  295.     ld    c,l        ;get Byte Count into BC
  296.  
  297.     ld    h,d
  298.     ld    l,e        ;get "source" address = Free_RAM
  299.  
  300.     inc    de        ;set "destination" address = Free_RAM + 1
  301.  
  302.     ldir            ;Zero memory.
  303.  
  304. ;This sequence loads up our data area in RAM:
  305.  
  306.     ld    hl,data_init
  307.     ld    de,nbuffers
  308.     ld    bc,data_size
  309.     ldir
  310.  
  311.  
  312. ; Set stack size and init free buffer list.
  313.  
  314.     ld    hl,0
  315.     add    hl,sp            ;get value of SP, high memory
  316.     ld    de,100            ;50 words for stack
  317.     or    a            ;clear carry
  318.     sbc    hl,de            ;now hl has "pseudo top of memory"
  319.     ld    de,bottom        ;"pseudo bottom of memory"
  320.     or    a
  321.     sbc    hl,de            ;hl now has size of available memory
  322.     rl    l            ;put MSB into carry
  323.     rl    h            ;put carry into LSB
  324.                     ;now h has number of buffers available
  325.     ld    a,h
  326.     ld    (nbuffers),a        ;save this number in memory
  327.  
  328.     ld    hl,bottom        ;beginning of buffer space
  329.     ld    (free),hl        ;now it's also top of free list
  330. ; init buffer free list
  331.     ld    b,a            ;get nbuffers (see above)
  332.     dec    b            ;because last one has 0 as "next"
  333. ibloop:
  334.     push    hl
  335.     ld    de,128
  336.     add    hl,de            ;HL has "next" pointer
  337.     ex    de,hl            ;DE has "next" pointer
  338.     pop    hl            ;HL now has pointer to current buffer
  339.  
  340.     ld    (hl),e            ;low byte of "next" pointer first
  341.     inc    hl
  342.     ld    (hl),d            ;now hi byte
  343.     inc    hl
  344.     xor    a
  345.     ld    (hl),a            ;zero out count field
  346.     inc    hl
  347.     ld    (hl),a            ;zero out # of bytes read field
  348.  
  349.     ex    de,hl            ;HL is now pointer to next buffer
  350.     djnz    ibloop            ;and init all the available buffers
  351.  
  352.     xor    a
  353.     ld    (hl),a            ;Last "next" address is 0
  354.     inc    hl
  355.     ld    (hl),a            ;ditto
  356.  
  357.     inc    hl
  358.     ld    (hl),a            ;zero out count field
  359.     inc    hl
  360.     ld    (hl),a            ;zero out # of bytes read field
  361.  
  362. ;init regs for ib_ext interrupt
  363.     exx
  364.     ld    bc,0            ;set prev state of SYNC pin,for 1200hz
  365.     ld    de,0            ;count of # of interrupts init
  366.     exx
  367.  
  368.     xor    a
  369.     ld    (RX_Allocated_Buffer),a        ;not receiving at this time
  370.  
  371.     ld    hl,TXQ_enables
  372.     ld    b,N_events
  373. E_clear:
  374.     ld    (hl),a            ; Turn off all the enables of all ...
  375.     inc    hl            ; ... possible events.
  376.     djnz    E_clear
  377.  
  378. ;init the routine addresses in our event table
  379.     ld    hl,R_Delay
  380.     ld    (TXQ_Addresses + 2*0),hl
  381.     ld    hl,R_SlotTime
  382.     ld    (TXQ_Addresses + 2*1),hl
  383.     ld    hl,R_Tail
  384.     ld    (TXQ_Addresses + 2*2),hl
  385.  
  386.  
  387.     ld    a,50
  388.     ld    (TXdelay),a        ; TX delay default is 500 ms
  389.      ld    a,64
  390.     ld    (Persistence),a        ; set default value for Persistence
  391.     ld    a,10
  392.     ld    (SlotTime),a        ; and Slot Time defaults to 100 ms
  393.     ld    a,3
  394.     ld    (TailTime),a        ;Tail Timer default
  395.  
  396.  
  397. ; Now have the CON and STA LEDs do a "dance".
  398.  
  399.     exx
  400.     push    hl
  401.     exx
  402.     pop    de        ;we saved the logicals telling us which LEDs
  403.                 ;to flash when we figured out the stacksize.
  404.                 ;This is how we know which LEDs to blink.
  405.  
  406.     ld    b,6        ;Do it 6 times (arbitrary as hell, but should
  407.                 ;be an even number so that the LEDs are off at
  408.                 ;the end of this mess...)
  409.     ld    hl,0        ;use HL as downcounter
  410. dance0:
  411.     ld    a,d
  412.     or    a
  413.     call    nz,CON_Flip
  414.     ld    a,e
  415.     or    a
  416.     call    nz,STA_Flip
  417. dance1:
  418.     dec    hl
  419.     ld    a,h
  420.     or    l
  421.     jp    nz,dance1
  422.  
  423.     djnz    dance0        ;do this 6 times  (3 "cycles")
  424.  
  425. ;Previous stuff showed that the download or boot worked properly...
  426.  
  427.  
  428.  
  429. ;We re-initialize the SIO ports so that we flush garbage chars that may have
  430. ;come in while we were diddling the LEDs.  This is necessary because unless we
  431. ;do this, then the A channel (modem) get RX overrun (esp if TNC was listening
  432. ;to noise) and RX overrun is VERY BAD - so bad, in fact, that I turn on both
  433. ;CON and STA and halt, because this situation should NEVER happen in normal
  434. ;use.  I flush the B (tty) channel in case anything was sent to it in mid-
  435. ;stream.
  436.  
  437.  
  438. ;Re-Init SIO.
  439.  
  440.     in    a,(A_ctl)        ;assure we are talking to ch 0
  441.     ld    c,A_ctl
  442.     ld    b,a_size
  443.     ld    hl,a_init
  444.     otir                ;init sync (modem) port
  445.  
  446. ;Re-Init Async port.
  447.  
  448.     in    a,(B_ctl)        ;assure we are talking to ch 0
  449.     ld    c,B_ctl
  450.     ld    b,b_size
  451.     ld    hl,b_init
  452.     otir                ;init async port & interrupt vector
  453.  
  454.  
  455. ; Prepare to load hi bits of interrupt vector
  456.  
  457.     ld    a,I_Register
  458.     ld    i,a            ;set interrupt page for mode 2 ints
  459.     im    2
  460.     ei                ;let 'em rip!
  461.  
  462. ;-----------------------------------------------------------------------------
  463. ; This is the background program.
  464. ; Note that since everything else is interrupt driven, and saves registers,
  465. ; this part of the code can use registers & expect values to stay.
  466.  
  467. Commutator_loop:
  468.     ld    a,(TX_outstanding)    ;if there are no outstanding TX...
  469.     or    a            ;...frames, then we don't have to...
  470.     jp    z,Scan_Check        ;...worry about Transmitter
  471.  
  472. ; If there are frames to transmit, we may have turned on TXdelay, or we may be
  473. ; transmitting a frame so check first.
  474. ; (This bug found late on 30 Sep 86)  The cleanest way to do 
  475. ; this is to check if we are keyed up.  If so, nothing else to do for now
  476. ; here.  This is the "Last Bug!"  Found at 11:59pm EDT on 30 Sep.
  477.  
  478.     ld    a,(A_WR5)
  479.     and    RTS
  480.     jp    nz,Scan_Check        ;if TX keyed up, nothing for us to
  481.                     ;do here!
  482.  
  483. ; else we've noticed that we've got some frame(s) to send.
  484. ; try to keyup TX
  485.  
  486.     ld    a,(Full_Duplex)
  487.     or    a
  488.     jp    nz,Key_Up        ;if Full Duplex, then there is no
  489.                     ;need to worry about all this silly
  490.                     ;slot time and persistence stuff!
  491.  
  492.     ld    a,(TXQE_SlotTime)    ;get SlotTime timer enable
  493.     or    a
  494.     jp    nz,Scan_Check        ;if we're waiting, keep waiting!
  495.  
  496. ;check if Carrier Detect is active
  497.     ld    a,(DCD_State)        ;DCD_State is set in interrupt routine
  498.     or    a
  499.     jp    nz,Scan_Check        ;If carrier active, wait it out
  500.  
  501. ;So, DCD is inactive; do persistence algorithm
  502.     ld    a,r            ;grab the Z-80 refresh register
  503.     add    a,a            ;double;now 0 <= A reg <= 254
  504.     ld    b,a            ;B holds our "random" number
  505.     ld    a,(Persistence)
  506.     sub    b            ;A reg = Persistence - Random #
  507.     jp    c,No_PTT        ;if (P-r) < 0 then no PTT now
  508.                     ; Note that P=255 means ALWAYS key up
  509.  
  510. ;OK, so we've won with the random number generator.  Keyup TX and start the
  511. ;TXdelay timer
  512.  
  513. Key_Up:
  514.     ld    a,(TXdelay)
  515.     ld    h,0
  516.     ld    l,a            ;HL is 16-bit value of TXdelay
  517.     ld    (TXQT_Delay),hl        ;Get timer value into timer slot
  518.     ld    a,1
  519.     ld    (TXQE_Delay),a        ;Enable this event
  520.  
  521.     ld    a,5
  522.     di                ;we need quite time here.
  523.     out    (A_ctl),a        ;;;Ready to write into WR5 of Ch A
  524.     ld    a,(A_WR5)
  525.     or    RTS            ;;;Turn on the PTT bit...
  526.     ld    (A_WR5),a        ;;;...in the memory copy of WR5
  527.     out    (A_ctl),a        ;;; Keyup transmitter
  528.     ei
  529.     jp    Scan_Check        ;That's all we do for now, we await
  530.                     ;TXdelay event
  531.  
  532. No_PTT:        ;since we lost on Random #, wait SlotTime before trying again
  533.     ld    a,(SlotTime)
  534.     ld    h,0
  535.     ld    l,a            ;HL has 16-bit version of SlotTime
  536.     ld    (TXQT_SlotTime),hl    ;Set up the timer value of this event
  537.     ld    a,1
  538.     ld    (TXQE_SlotTime),a    ;and enable this event
  539. ; Note that this code does not have to be interrupt protected because we
  540. ; really don't care if the slot timer is decremented between being loaded
  541. ; and being enabled.
  542.  
  543. Scan_Check:
  544.     ld    hl,TXQ_enables        ; gear up to check timer routines
  545.     ld    ix,TXQ_timers
  546.     ld    iy,TXQ_addresses
  547.     ld    de,2            ;bump ix & iy by twos
  548.     ld    b,N_events        ;Number of possible events
  549. scan_top:
  550.     ld    a,(hl)
  551.     or    a
  552.     jp    z,scan_bottom        ;if not enabled, check next one
  553.  
  554. ;else is enabled.  Timer expired?
  555.     ld    a,(ix+1)
  556.     ld    c,a            ;save MS byte for possible use later
  557.     or    (ix)
  558.     jr    z,scan_fire        ;fire this if we are at 0 count
  559.  
  560.     ld    a,c
  561.     or    a            ; saves us some time doing it this way
  562.     jp    p,scan_bottom        ; or fire if we are negative
  563.  
  564. scan_fire:
  565.     xor    a
  566.     ld    (hl),a            ;disable this event as it fires
  567.     push    hl
  568.     ld    hl,scan_return        ;load up routine return address
  569.     push    hl            ;save as return address on stack
  570.     ld    h,(iy+1)
  571.     ld    l,(iy)            ;get address of routine to "call"
  572.     jp    (hl)            ;"call" this routine
  573.  
  574. scan_return:                ;where all routines return
  575.     pop    hl            ;get original HL back
  576.  
  577. scan_bottom:
  578.     inc    hl            ;increment enable table pointer
  579.     add    ix,de            ;keep timer table pointer in step
  580.     add    iy,de            ;keep routine table pointer in step
  581.     djnz    scan_top        ;look at all entries in tables
  582.  
  583.  
  584. ;Now see if we need to start an output to RS-232 (host) port
  585.     ld    a,(out_started)
  586.     or    a            ;also clears carry (see below) 
  587.     jp    nz,Commutator_loop    ;if output started, nothing to do
  588.  
  589. ; else we should check to see if we need to start an output
  590.     di
  591.     call    CON_off            ;;;
  592.     ld    hl,(out_head_cbuf)    ;;;grab current top of circ buf ptr
  593.     ld    de,(out_tail_cbuf)    ;;;and where the next free buf ptr is
  594.     ei
  595.                     ;interrupt protect the pickup of the
  596.                     ;two pointers 3 Feb 87
  597.     or    a
  598.     sbc    hl,de
  599.     jp    z,Commutator_loop    ;if the same, nothing to do
  600.  
  601. ;else we need to start an output
  602.     di                ;interrupt protect this section,
  603.                     ;although I'm not sure it needs it...
  604.                     ;3 Feb 87
  605.                     ;note: it should already BE done!
  606.     ld    hl,(out_head_cbuf)    ;;;get pointer to next cbuf to output
  607.     ld    e,(hl)
  608.     inc    hl
  609.     ld    d,(hl)            ;;;DE has pointer to buffer chain
  610.     ld    (out_chain_head),de    ;;;set in interrupt routine's place
  611.     ld    a,1
  612.     ld    (out_started),a        ;;;yes, output started
  613.  
  614.     call    CON_on    
  615. cl_0:
  616.     in    a,(B_ctl)        ;;;look at RR0
  617.     and    TBE            ;;;isolate the TBE bit
  618.     jr    z,cl_0            ;;;wait for transmitter to get done
  619.  
  620.     ld    a,FEND
  621.     out    (B_dat),a        ;;;send FEND character (start txing)
  622.     ei
  623.  
  624.     jp    Commutator_loop        ;keep looking for new opportunity
  625.  
  626. ;*****************************************************************************
  627. ; Timer-driven Events
  628. ;*****************************************************************************
  629.  
  630. ;-----------------------------------------------------------------------------
  631. R_Delay:    ; This routine executes when the TX Delay timer expires.
  632.     push    af
  633.     push    bc
  634.     push    de
  635.     push    hl
  636.     di
  637.     call    TXnext_CBuf        ;gets HL to point to buffer chain, and
  638.                     ;sets TX_Chain_Head for the interrupt
  639.                     ;routine
  640.     ld    a,80h
  641.     out    (A_ctl),a        ;;; reset TX CRC
  642.     call    getchar            ;;; getchar needs int. protection
  643.     out    (A_dat),a        ;;; Ship this char to TX modem
  644.     ld    a,1
  645.     ld    (TX_Started),a        ;;; and, yes Virgina, we've started TX
  646.     ld    a,0c0h
  647.     out    (A_ctl),a        ;;; reset TX underrun/EOM latch
  648.     pop    hl
  649.     pop    de
  650.     pop    bc
  651.     pop    af
  652.     ei
  653.     ret
  654.  
  655. ;-----------------------------------------------------------------------------
  656.  
  657. R_SlotTime:    ;when SlotTime event timer expires, come here.
  658.     ret                ; we were just waiting, so nothing
  659.                     ; else to do here (!)
  660.  
  661. ;-----------------------------------------------------------------------------
  662.  
  663. R_Tail:        ;When tail timer times out, turn off the TX
  664.  
  665.     push    af
  666.     ld    a,5            ;ready to write to WR5 of Ch A
  667.     di                ;;;must have atomic use of A_WR5 & SIO
  668.     out    (A_ctl),a        ;;;Next char to A_ctl goes to WR5
  669.     ld    a,(A_WR5)            ;;;grab A_WR5
  670.     and    NOT RTS            ;;;turn off RTS bit there
  671.     ld    (A_WR5),a            ;;;keep memory copy updated
  672.     out    (A_ctl),a        ;;;and turn off TX now
  673.     ei
  674.     pop    af
  675.     ret
  676.  
  677. ;    include    IA.MAC            ;Modem interrupt catchers
  678. ;;;---------------------------------------------------------------------------
  679. ia_tbe:
  680.     push    af
  681.     push    hl
  682.     ld    a,(TX_Started)
  683.     or    a
  684.     jp    z,ia_t2        ;;; previous frame finished
  685.  
  686.     ld    hl,(TX_Chain_Head)
  687.     call    getchar
  688.     ld    (TX_Chain_Head),hl    ;;; must keep this pointer updated
  689.     jr    z,ia_t1        ;;; no more to send
  690.  
  691.     out    (A_dat),a    ;;; else ship this char out
  692. ia_t9:
  693.     pop    hl
  694.     pop    af
  695.     ei
  696.     reti            ;;; just return from these interrupts
  697.  
  698. ia_t1:
  699. ;    halt            ;;;if it gets here, halt
  700.     xor    a
  701.     ld    (TX_Started),a        ;;; TX is NOT started
  702.     ld    hl,TX_Outstanding    ;;; make is so that one fewer frames
  703.                     ;;; NOT "(TX_Outstanding)" (!) 29 Sep
  704.     dec    (hl)            ;;; are outstanding
  705.     ld    a,28h
  706.     out    (A_ctl),a        ;;; reset TX interrupt pending
  707.     jp    ia_t9
  708.  
  709. ;;;previous frame is done, SIO now sending a flag.  More?
  710. ia_t2:
  711.     ld    a,(TX_Outstanding)
  712.     or    a
  713.     jp    nz,ia_t21        ;;;if more to send, go there
  714.  
  715. ;;; else we're done here, clean up.
  716.     ld    a,28h
  717.     out    (A_ctl),a        ;;; Reset TX interrupt pending
  718.  
  719. ;start Tail timer event
  720.     ld    a,(TailTime)        ;;; { bug found 30 Sep.  It was:
  721.     ld    h,0            ;;; "ld hl,(TailTime)"
  722.     ld    l,a            ;;; [ouch!] }
  723.     ld    (TXQT_Tail),hl        ;;; wait for CRC to clear TX
  724.     ld    a,1            ;;; 8.33 ms/char at 1200 bps
  725.     ld    (TXQE_Tail),a        ;;; TailTime value SHOULD be >=2.
  726.     jp    ia_t9
  727.  
  728. ia_t21:            ;start up next frame
  729.     call    TXnext_CBuf        ;;; get the next buffer chain pointer
  730.                     ;;; setup HL and TX_Chain_Head
  731.     ld    a,80h
  732.     out    (A_ctl),a        ;;; reset TX CRC generator
  733.     call    getchar
  734.     out    (A_dat),a        ;;;get 1st char of next frame
  735.     ld    a,1
  736.     ld    (TX_Started),a        ;;; TX started again
  737.     ld    a,0c0h
  738.     out    (A_ctl),a        ;;; reset TX underrun/EOM latch
  739.     jp    ia_t9
  740.  
  741. ;;;---------------------------------------------------------------------------
  742. ;;; Got a character from the SIO RX interrupt, deal with it
  743. ;;; Extensive mods 3 Feb 87 to be in line with what I now know about SIO...
  744.  
  745. ia_rca:
  746.     push    af
  747.     push    hl
  748.  
  749.     ld    a,(RX_Allocated_Buffer)
  750.     or    a
  751.     jp    nz,ia_rc7    ;;; Go there if we are in "receiving" state
  752.  
  753. ;else we are not yet receiving, so allocate buffer & make us "receiving"
  754.  
  755.     call    allocate_buffer    ;;; get a new buffer
  756. ;    jp    z,ia_rc5    ;;; NO ROOM, flush this frame
  757.  
  758. ;;; if got a buffer, insert this character.
  759. ;;; after doing initial buffer setup.
  760.  
  761. ia_rc6:
  762.     ld    (RX_head),hl    ;;; save chain head address (1st buffer)
  763.     ld    (RX_buf),hl    ;;; tuck away addr of our current buffer
  764.     ld    a,TRUE
  765.     ld    (RX_Allocated_Buffer),a    ;;; and mark that we are receiving
  766.  
  767.     xor    a
  768.     call    putchar        ;;; SLIP' frame "type" field here (Always 0)
  769.  
  770. ia_rc7:
  771.     ld    hl,(RX_buf)    ;;; load up address of our current RX buffer
  772.     in    a,(A_dat)    ;;; grab the pending character
  773.     call    putchar        ;;; and stuff in this particular buffer
  774.     ld    (RX_buf),hl    ;;; HL might have changed in putchar()
  775.  
  776. ;;;*** NOTE!  There is a problem here!  If putchar() has no more room, then
  777. ;;; we need to flush all frames so far accumulated & go into RX_flushing
  778. ;;; state !!! 3 Feb 87
  779.  
  780. ia_rc9:
  781.     pop    hl
  782.     pop    af
  783.  
  784.     ei
  785.     reti            ;;; nothing else to do here
  786.  
  787.  
  788. ;;; if no room, flush this frame (sigh)
  789. ;ia_rc5:
  790. ;    ld    a,TRUE
  791. ;    ld    (RX_flushing),a    ;;; we are in the midst of flushing this frame
  792. ia_rc2:
  793. ;    call    STA_on        ;;;ddd Note that we are in flushing state
  794. ;    in    a,(a_dat)
  795. ;    in    a,(a_dat)
  796. ;    in    a,(a_dat)
  797. ;    in    a,(a_dat)    ;;; empty SIO Silo
  798. ;
  799. ;    jp    ia_rc9
  800.  
  801.  
  802. ;;;---------------------------------------------------------------------------
  803. ;;; From out point of view, this interrupt is only interesting because it
  804. ;;; tells us if we're at end of frame.
  805. ia_special:
  806.     push    af
  807.     push    hl        ;;; regs we'll need
  808.  
  809.     ld    a,1
  810.     out    (A_ctl),a    ;;; ready to read RR1
  811.     in    a,(A_ctl)    ;;; OK, grab RR1
  812.  
  813. ;;; First check if RX overrun.  This is VERY BAD, so halt.
  814.     bit    5,a
  815.     jp    z,ia_sp0    ;;; Most of the time (all the time?) go there
  816.  
  817.     call    CON_on
  818.     call    STA_on
  819.     halt
  820.  
  821. ia_sp0:
  822.     bit    7,a        ;;; check state of End of Frame bit
  823.     jp    z,ia_sp8    ;;; Else something weird happened - probably
  824.                 ;;; RX overrun. In any case, flush this frame.
  825.                 ;;; error reset & then exit
  826.                 ;;; that is, treat like it was a CRC error
  827.  
  828. ;;; If End of Frame, check CRC bit for valid.
  829. ia_sp1:
  830.     bit    6,a        ;;; Check CRC error bit
  831.     jp    nz,ia_sp8    ;;; If CRC error bit is on, then was CRC error
  832.  
  833. ;;; First ensure that we indeed have a buffer allocated...
  834.     ld    a,(RX_Allocated_Buffer)
  835.     or    a
  836.     jp    z,ia_sp9    ;;; if no buffer allocated, ignore this.
  837.  
  838. ;;; Else this was a good frame, and we should ship it out to host
  839. ;;; Leave the first CRC character at end of buffer chain in the buffer, as
  840. ;;; getchar() will flush it.
  841.  
  842.     ld    hl,(RX_head)
  843.     call    out_queue_insert    ;;; Shove this buffer string onto
  844.                     ;;; output queue
  845.     xor    a
  846.     ld    (RX_Allocated_Buffer),a    ;;; We don't have a buffer allocated
  847.                     ;;; for the next frame...
  848.     jp    ia_sp9
  849.  
  850. ;;; get here if there was a bad CRC
  851. ia_sp8:
  852.     ld    a,(RX_Allocated_Buffer)    ;;; If we don't have any buffers
  853.                     ;;; allocated, then 
  854.     or    a        ;;;8 Feb - SET CONDITION CODES !!!!!!
  855.     jp    z,ia_sp9    ;;; we MUST NOT "release" them !!! 10 Sep 86
  856.                 ;;; if they are not allocated !!!
  857. ia_spf:
  858.     xor    a
  859.     ld    (RX_Allocated_Buffer),a    ;;; not receiving if we have bad CRC
  860.     ld    hl,(RX_head)
  861.     call    free_chain    ;;; free up all buffer(s)
  862.  
  863. ia_sp9:
  864.     ld    a,30h        ;;; error reset
  865.     out    (A_ctl),a
  866.     in    a,(A_dat)    ;;; Avoid spurious RCA interrupt
  867.  
  868. ;    in    a,(A_dat)    ;;; Avoid spurious RCA interrupt
  869. ;    in    a,(A_dat)    ;;; Avoid spurious RCA interrupt
  870. ;    in    a,(A_dat)    ;;; Avoid spurious RCA interrupt
  871. ;                ;;; and flush silo
  872.     pop    hl
  873.     pop    af
  874.  
  875.     ei
  876.     reti
  877.  
  878. ;;;---------------------------------------------------------------------------
  879. ;;; for ext/status interrupts on Modem, get DCD state into memory, and
  880. ;;; deallocate any spurious buffers (buffer stuff done 30 Sep 86).
  881. ia_ext:
  882.     push    af
  883.  
  884.     ld    a,10h        ;;; reset ext/status interrupts
  885.     out    (A_ctl),a
  886.     in    a,(A_ctl)    ;;; grab RR0
  887.     and    DCD
  888.     ld    (DCD_State),a    ;;;save for TX keyup DCD detect.  Is 0 if DCD
  889.                 ;;;is not active, or non-zero if it is active.
  890.  
  891.  
  892.     ld    a,(RX_Allocated_Buffer)    ;;; if we are not in the
  893.                     ;;; receiving state...
  894.     or    a        ;;; then there are no allocated buffers and...
  895.     jp    z,ia_ex9    ;;; we MUST NOT "release" them !!! 10 Sep 86
  896.                 ;;; if no buffers allocated !!!
  897.     xor    a
  898.     ld    (RX_Allocated_Buffer),a    ;;; not receiving
  899.     push    hl
  900.     ld    hl,(RX_head)
  901.     call    free_chain    ;;; free up all buffer(s)
  902.     pop    hl
  903.  
  904.  
  905. ia_ex9:
  906.     pop    af
  907.  
  908.     ei
  909.     reti
  910.  
  911. ;    include    IB.MAC            ;TTY interrupt catchers
  912.  
  913. ;;;---------------------------------------------------------------------------
  914. ;;; we get here whenever -cts, -dcd or -sync inputs change, as well as break
  915. ;;; detection. Since -dcd
  916. ;;; is always tied to +5 volts, we need only worry about -cts and -sync.
  917. ;;; -cts is wired to pin 20, DTR, of the RS232 connector, and is supposed to
  918. ;;; be used for host to TNC handshaking; we ignore this transition (We assume
  919. ;;; that the host is always ready).  We also ignore break detection.  We are
  920. ;;; only interested in -sync transitions, so we can keep time.
  921. ;;; NOTE!  This is the ONLY routine that is allowed to use the other reg set!!
  922. ;;; deal with break detection...
  923.  
  924. sync_hunt    equ    10h
  925. ib_ext:
  926.     ex    af,af'
  927.     exx            ;;; we want the other registers
  928.     ld    a,10h
  929.     out    (B_ctl),a    ;;; reset ext/status interrupts
  930.     in    a,(B_ctl)    ;;; grab RR0
  931.     ld    d,a        ;;; Hold it for a moment...
  932.     and    sync_hunt    ;;; isolate this bit
  933.     jp    z,ib_s0
  934. ;else sync/hunt is a 1
  935.     ld    a,c
  936.     or    a
  937.     jp    z,ib_s1        ;;; go here if state of sync/hunt changed
  938.  
  939.  
  940. ;;; Here if sync/hunt bit did NOT change - maybe something else did....
  941. ib_s9:
  942.     ld    a,d        ;;; retreive RRO from above
  943.     and    Break_Abort    ;;; Check if we are doing a break/abort thing
  944.     jp    z,ib_NBA    ;;; There if No break/abort
  945.  
  946. ;;; Else Break/Abort bit on, note state change...
  947.     ld    a,1
  948.     ld    (in_break),a    ;;; save in mem  (probably can use E reg...)
  949.     in    a,(B_dat)    ;;; clear out any null character from buffer
  950.     jp    ib_BOK        ;;; Break OK for now...
  951.  
  952. ib_NBA:        ;;;if no break/abort, check if we are in break/abort state.
  953.     ld    a,(in_break)
  954.     or    a
  955.     jp    z,ib_BOK    ;;; Nothing going on, Break OK
  956.  
  957. ;;; Else we were in break mode, and this is the tail end of a break.
  958.     xor    a
  959.     ld    (in_break),a
  960.     in    a,(B_dat)    ;;; discard the single extraneous null
  961. ib_BOK:
  962. ib_s99:
  963.     ex    af,af'
  964.     exx
  965.     ei
  966.     reti            ;;; else something else & we don't care
  967. ib_s0:                ;;; sync/hunt is a 0
  968.     ld    a,c
  969.     or    a
  970.     jp    nz,ib_s1a    ;;; go here if sync/hunt changed
  971.     jp    ib_s9        ;;; else not interested, forget it
  972.  
  973. ;get here if state of sync/hunt changed
  974. ib_s1:
  975.     ld    c,1
  976.     jp    ib_s1b
  977. ib_s1a:                ;;; first fix up C for next tick
  978.     ld    c,0
  979. ib_s1b:
  980. ;;; Here when we've seen a real "clock tick" & dealt with C reg
  981.     inc    b
  982.     ld    a,b
  983.     cp    12
  984.     jp    nz,ib_s99        ;;; we act on every 12th clock tick...
  985.     ld    b,0            ;;; so reload divisor. This give us an
  986.                     ;;; effective interrupt rate of 100 Hz
  987.  
  988. ;;; Decrement all the timers
  989.  
  990.     ld    hl,(TXQ_timers+2*0)    ;;; Get first timer value, and ...
  991.     dec    hl            ;;; ... decrement it as required.
  992.     ld    (TXQ_timers+2*0),hl
  993.  
  994.     ld    hl,(TXQ_timers+2*1)    ;;; Get second timer value, and ...
  995.     dec    hl            ;;; ... decrement it as required.
  996.     ld    (TXQ_timers+2*1),hl
  997.  
  998.     ld    hl,(TXQ_timers+2*2)    ;;; Get third timer value, and ...
  999.     dec    hl            ;;; ... decrement it as required.
  1000.     ld    (TXQ_timers+2*2),hl
  1001.  
  1002.     jp    ib_s99
  1003.  
  1004.  
  1005. ;;;---------------------------------------------------------------------------
  1006. ib_special:
  1007.     push    af
  1008. ib_sp9:            ;;; Normal exit
  1009.     ld    a,30h        ;;; error reset
  1010.     out    (B_ctl),a
  1011.     pop    af
  1012.     ei
  1013.     reti
  1014.  
  1015. ;;;---------------------------------------------------------------------------
  1016. ;;; The TX has become empty, shove a new character out
  1017. ib_tbe:
  1018.     push    af        ;;; new char will return in A
  1019.     push    hl
  1020.  
  1021.     ld    a,(ib_esc_mode)
  1022.     or    a
  1023.     jp    z,ib_t1        ;;; not escaped, so go here
  1024. ;;; else we are escaped, so send escaped char
  1025.     ld    a,(ib_char)    ;;; char which follows escape
  1026.     or    a
  1027.     jp    z,ib_t2        ;;; special case if at end of frame, clean up
  1028.     out    (B_dat),a
  1029.     xor    a
  1030.     ld    (ib_esc_mode),a    ;;; get out of escaped mode
  1031.     jp    ib_t9        ;;; all for now...
  1032. ib_t1:
  1033.     ld    hl,(out_chain_head) ;;; we are currently on this buffer, as...
  1034.     call    getchar        ;;; getchar() needs to know
  1035.     ld    (out_chain_head),hl ;;; maybe HL changed, so save it in case
  1036.  
  1037.     jp    z,ib_tdone    ;;; if no more chars, deal with this
  1038.     cp    FESC
  1039.     jp    z,ib_t1a    ;;; deal with FESC char in data stream
  1040.     cp    FEND
  1041.     jp    z,ib_t1b    ;;; deal with FEND char in data stream
  1042. ;;; else this char is nothing special, so shove it out
  1043.  
  1044.     out    (B_dat),a    ;;; shove it out
  1045.     jp    ib_t9        ;;; if this is not last char, all for now
  1046.  
  1047. ;;; else this is last char, send FEND
  1048. ib_tdone:
  1049.     ld    a,FEND
  1050.     out    (B_dat),a
  1051.     ld    a,1
  1052.     ld    (ib_esc_mode),a    ;;; set special escaped mode by...
  1053.     xor    a
  1054.     ld    (ib_char),a    ;;;... making escaped char a 0
  1055.     jp    ib_t9        ;;; all till TX Buffer goes empty again.
  1056.  
  1057. ; here if are completely done sending frame
  1058. ib_t2:
  1059.     push    de        ;;; need this for a moment
  1060.     ld    hl,(out_head_cbuf)
  1061.     inc    hl
  1062.     inc    hl        
  1063.     ld    de,out_bottom
  1064.     or    a
  1065.     push    hl
  1066.     sbc    hl,de
  1067.     pop    hl        ;;; this may be the one we want
  1068.     pop    de
  1069.     jp    nz,ib_t2a    ;;; yes it is!
  1070.  
  1071.     ld    hl,out_top    ;;; else, make a circular buffer
  1072. ib_t2a:
  1073.     ld    (out_head_cbuf),hl ;;; we will work on this one next
  1074.     xor    a
  1075.     ld    (out_started),a    ;;; not doing outputs anymore
  1076.     ld    (ib_esc_mode),a    ;;; !!! NOT IN ESCAPED MODE ANYMORE !!!
  1077.  
  1078.     ld    a,28h        ;;; NEEDED for ASYNC
  1079.     out    (B_ctl),a    ;;; reset TX interrupt pending
  1080.  
  1081. ib_t9:
  1082.     pop    hl
  1083.     pop    af
  1084.     ei
  1085.     reti            ;;; now get our butts out of here...
  1086.  
  1087. ;;; here is FESC in data stream
  1088. ib_t1a:
  1089.     out    (B_dat),a    ;;; Ship FESC character to port
  1090.     ld    a,TFESC        ;;; ready what will be next char
  1091. ib_t1z:
  1092.     ld    (ib_char),a    ;;; set char for next time
  1093.     ld    a,1
  1094.     ld    (ib_esc_mode),a    ;;; we are in escaped mode
  1095.     jp    ib_t9        ;;; all for now
  1096.  
  1097. ;;; here is FEND in data stream
  1098. ib_t1b:
  1099.     ld    a,FESC
  1100.     out    (B_dat),a
  1101.     ld    a,TFEND
  1102.     jp    ib_t1z        ;;; rest is same as FESC case
  1103.  
  1104.  
  1105. ;;;---------------------------------------------------------------------------
  1106. ;;; Got a char from the TTY port, deal with it.
  1107.  
  1108. ib_rca:
  1109.     push    af
  1110.  
  1111.     in    a,(B_ctl)    ;;; Read RR0; force reg pointer to be 0
  1112.     ld    a,1
  1113.     out    (B_ctl),a    ;;; ready to read RR1
  1114.     in    a,(B_ctl)    ;;; Grab RR1
  1115.     and    Framing_Error    ;;; Isolate the FE bit
  1116.     jp    z,ib_Rtop    ;;; No Framing Error, so process this char
  1117.  
  1118. ;;; Else we have a Framing Error - Ignore this char & flush this frame...
  1119.     call    STA_off        ;;; Off with the LED!
  1120.     in    a,(B_dat)    ;;; Flush erroneous character
  1121.     xor    a
  1122.     ld    (In_state),a    ;;; Force receiver to look for FEND
  1123.     ld    a,(In_Allocated_Buffer)
  1124.     or    a
  1125.     jp    z,ib_rc9    ;;; If no buffer is allocated, done; Exit.
  1126.  
  1127. ;;; Else we were receiving a data SLIP frame, so flush it.
  1128.     push    hl
  1129.     ld    hl,(In_head)
  1130.     call    free_chain    ;;; Dump these buffers back to free list
  1131.     pop    hl
  1132.     jp    ib_rc9        ;;; And get out of here!
  1133.  
  1134. ib_rTop:
  1135.     ld    a,(In_state)    ;;; get our state machine value
  1136.     or    a
  1137.     jr    z,ib_r0        ;;; in state 0, waiting for FEND
  1138.     cp    1
  1139.     jr    z,ib_r1        ;;; in state 1, saw FEND
  1140.     cp    2
  1141.     jp    z,ib_r2        ;;; in state 2, data to follow
  1142.     cp    3
  1143.     jp    z,ib_r3        ;;; saw FESC, expecting TFESC or TFEND
  1144.     cp    10
  1145.     jp    z,ib_r10    ;;; Expecting TXdelay
  1146.     cp    20
  1147.     jp    z,ib_r20    ;;; Expecting P value
  1148.     cp    30
  1149.     jp    z,ib_r30    ;;; Expecting SlotTime value
  1150.     cp    40
  1151.     jp    z,ib_r40    ;;; Expecting TailTime value
  1152.     cp    50
  1153.     jp    z,ib_r50    ;;; Expecting Full/Half duplex value
  1154.  
  1155. ;else we don't know what happened, ignore it.
  1156. ib_rcjunk:
  1157.     in    a,(B_dat)
  1158.     xor    a
  1159.     ld    (In_State),a    ;;;go into In_State 0, FEND hunt
  1160. ib_rc9:
  1161.     pop    af        ;;; throw it away, we don't need junk
  1162.     ei
  1163.     reti
  1164.  
  1165. ;;; Here if we are hunting for FEND character
  1166. ib_r0:
  1167.     call    STA_off
  1168.  
  1169.     in    a,(B_dat)
  1170.     cp    FEND
  1171.     jp    nz,ib_rc9    ;;; if we didn't see an FEND, keep looking
  1172.  
  1173. ;;; else is an FEND, change state
  1174.     ld    a,1
  1175.     ld    (In_state),a
  1176.     jp    ib_rc9
  1177.  
  1178. ;;; Get here if we've seen FEND character; look for command byte
  1179. ib_r1:
  1180.     call    STA_off
  1181.     in    a,(B_dat)
  1182.     cp    FEND
  1183.     jp    z,ib_rc9    ;;; Just another FEND, keep looking for cmd
  1184.  
  1185.     call    STA_on        ;;;getting valid SLIP; show in STA LED
  1186.  
  1187. ;;; Here if we DO NOT have an FEND (expecting command byte)
  1188.     or    a
  1189.     jp    z,ib_r1a    ;;; 0 command means data will follow
  1190.     cp    1
  1191.     jp    z,ib_r1b    ;;; 1 command means TXdelay will follow
  1192.     cp    2
  1193.     jp    z,ib_r1c    ;;; 2 command means P(Persistence) will follow
  1194.     cp    3
  1195.     jp    z,ib_r1d    ;;; 3 command means Slot Time will follow
  1196.     cp    4
  1197.     jp    z,ib_r1e    ;;; 4 command means TailTime to follow
  1198.     cp    5
  1199.     jp    z,ib_r1f    ;;; 5 command means Full/Half duplex to come
  1200.  
  1201. ;;; Here if we receive bogus command byte, flush rest of frame
  1202.  
  1203.     call    STA_off        ;;;bogosity, so turn off STA LED
  1204.  
  1205.     xor    a
  1206.     ld    (In_state),a    ;;; go to state which looks for FEND
  1207.     jp    ib_rc9
  1208.  
  1209. ;;; Data are expected, change state
  1210. ib_r1a:
  1211.     ld    a,2
  1212.     ld    (In_state),a
  1213.     jp    ib_rc9
  1214.  
  1215. ;;; TXdelay to follow, change state
  1216. ib_r1b:
  1217.     ld    a,10
  1218.     ld    (In_state),a
  1219.     jp    ib_rc9
  1220.  
  1221. ;;; P to follow, change state
  1222. ib_r1c:
  1223.     ld    a,20
  1224.     ld    (In_state),a
  1225.     jp    ib_rc9
  1226.  
  1227. ;;; SlotTime to follow, change state
  1228. ib_r1d:
  1229.     ld    a,30
  1230.     ld    (In_state),a
  1231.     jp    ib_rc9
  1232.  
  1233. ;;; TailTime to follow, change state
  1234. ib_r1e:
  1235.     ld    a,40
  1236.     ld    (In_state),a
  1237.     jp    ib_rc9
  1238.  
  1239.  
  1240. ;;; Full/Half Duplex to follow, change state
  1241. ib_r1f:
  1242.     ld    a,50
  1243.     ld    (In_state),a
  1244.     jp    ib_rc9
  1245.  
  1246.  
  1247. ;;; These bytes are data
  1248. ib_r2:
  1249.     in    a,(B_dat)
  1250.     cp    FEND
  1251.     jr    z,ib_r2b    ;;; FEND means to queue this buffer
  1252.     push    af        ;;; Save the char we read on stack for a bit..
  1253.  
  1254.     ld    a,(In_Allocated_Buffer)
  1255.     or    a
  1256.     jp    nz,ib_r2c    ;;; if we already allocated buffer
  1257.  
  1258.     push    hl
  1259.     call    allocate_buffer    ;;; get our initial buffer to mess with
  1260.     jp    nz,ib_r22
  1261.  
  1262. ;;;else no room, flush this frame
  1263.     pop    hl        ;;; keep stack tidy
  1264.     xor    a
  1265.     ld    (In_State),a
  1266.     jp    ib_rc9
  1267.  
  1268. ib_r22:
  1269.     ld    a,1
  1270.     ld    (In_Allocated_Buffer),a    ;;; make ourselves active
  1271.  
  1272.     ld    (In_buffer),hl
  1273.     ld    (In_head),hl    ;;; save current & head of chain pointers
  1274.     pop    hl
  1275.  
  1276. ib_r2c:
  1277.     pop    af        ;;; Retreive the data char we just got...
  1278.     cp    FESC
  1279.     jr    z,ib_r2a    ;;; If FESC in data stream, switch state
  1280.  
  1281.     push    hl
  1282.     ld    hl,(In_buffer)
  1283.     call    putchar        ;;; shove this character into our buffer
  1284.     ld    (In_buffer),hl    ;;; save in case HL changed
  1285.     pop    hl
  1286.     jp    ib_rc9        ;;; done so far
  1287.  
  1288. ;;; FESC character seen while grabbing data
  1289. ib_r2a:
  1290.     ld    a,3
  1291.     ld    (In_state),a    ;;; go to this other state
  1292.     jp    ib_rc9
  1293.  
  1294. ;;; FEND character seen while grabbing data
  1295. ib_r2b:
  1296.     ld    a,(In_Allocated_Buffer)
  1297.     or    a
  1298.     jr    z,ib_r2z    ;;; No bytes accumulated, so is null frame
  1299.  
  1300. ;;; else we must ship this frame to TX
  1301.     push    hl        ;;; This bug found 29 Sep (must save HL !!!)
  1302.     ld    hl,(In_Buffer)
  1303.     call    putchar        ;;; put a garbage character at the end of
  1304.                 ;;; last buffer because getchar() will strip
  1305.                 ;;; it. Hack needed because of RX use of
  1306.                 ;;; putchar/getchar.
  1307.      ld    hl,(In_head)
  1308.     call    TX_queue_insert
  1309.     pop    hl
  1310.     xor    a
  1311.     ld    (In_Allocated_Buffer),a    ;;; input no longer active
  1312. ib_r2z:                ;;; entry point for null frame
  1313.     ld    a,1        ;;; Keep as was, FENDs only at end in v.32
  1314.     ld    (In_state),a    ;;; go look for another frame
  1315.  
  1316.     call    STA_off        ;;;done getting this frame, turn STA LED off
  1317.  
  1318.     jp    ib_rc9
  1319.  
  1320.  
  1321. ;;; here if we've seen FESC in data stream
  1322. ib_r3:
  1323.     in    a,(B_dat)
  1324.     cp    TFESC
  1325.     jr    z,ib_r3a
  1326.     cp    TFEND
  1327.     jr    z,ib_r3b
  1328.  
  1329. ;;; Else we don't know what the hell it is, so ignore & keep collecting bytes
  1330.     ld    a,2
  1331.     ld    (In_state),a    ;;; go back into "data receiving" state
  1332.     jp    ib_rc9
  1333.  
  1334. ;;; here if we've seen TFESC after an FESC in data stream; write an FESC
  1335. ib_r3a:
  1336.     ld    a,FESC
  1337. ib_r3z:
  1338.     push    hl
  1339.     ld    hl,(In_buffer)
  1340.     call    putchar
  1341.     ld    (In_buffer),hl
  1342.     pop    hl
  1343.     ld    a,2
  1344.     ld    (In_state),a        ;;; get out of escaped mode
  1345.     jp    ib_rc9
  1346.  
  1347. ;;; Here if we've seen TFEND after FESC in data stream; write FEND
  1348. ib_r3b:
  1349.     ld    a,FEND
  1350.     jp    ib_r3z            ;;; rest is same as for TFESC case
  1351.  
  1352.  
  1353. ;;; This character is interpreted as TXdelay
  1354. ib_r10:
  1355.     in    a,(B_dat)
  1356.     ld    (TXdelay),a
  1357.     xor    a    
  1358.     ld    (In_state),a    ;;; go back to FEND hunt state
  1359.     jp    ib_rc9
  1360.  
  1361. ;;; This charcter is P, Persistence value
  1362. ib_r20:
  1363.     in    a,(B_dat)
  1364.     ld    (Persistence),a
  1365.     xor    a
  1366.     ld    (In_state),a    ;;; go back to FEND hunt state
  1367.     jp    ib_rc9
  1368.  
  1369. ;;; This character is SlotTime value
  1370. ib_r30:
  1371.     in    a,(B_dat)
  1372.     ld    (SlotTime),a
  1373.     xor    a
  1374.     ld    (In_state),a    ;;; go back to FEND hunt state
  1375.     jp    ib_rc9
  1376.  
  1377.  
  1378. ;;; This character is TailTime value
  1379. ib_r40:
  1380.     in    a,(B_dat)
  1381.     ld    (TailTime),a
  1382.     xor    a
  1383.     ld    (In_state),a    ;;; go back to FEND hunt state
  1384.     jp    ib_rc9
  1385.  
  1386.  
  1387. ;;; This character is Full/Half Duplex value
  1388. ;;; 0 means Half Duplex, non-zero means Full Duplex
  1389. ib_r50:
  1390.     in    a,(B_dat)
  1391.     ld    (Full_Duplex),a
  1392.     xor    a
  1393.     ld    (In_state),a    ;;; go back to FEND hunt state
  1394.     jp    ib_rc9
  1395.  
  1396. ;    include    BUFFERS.MAC        ;all buffer-related stuff in here
  1397.                     ;plus all (eventually) variables
  1398. ;
  1399. ; The buffer list is kept from "bottom" to the end of RAM.  The format of the
  1400. ; buffers is:
  1401.  
  1402. ;+------+--------+-------+---------------------------------------------------+
  1403. ;| next | Nbytes | Nread | data                             |
  1404. ;+------+--------+-------+---------------------------------------------------+
  1405. ;
  1406. ; 2 bytes 1 byte   1 byte  124 bytes  (Total 128 bytes)
  1407.  
  1408. ; next     Pointer to next buffer on this buffer chain (or 0 if no more)
  1409. ; Nbytes Number of bytes in this buffer that are valid
  1410. ; Nread  Number of bytes read from this buffer (used by getchar)
  1411. ; data   124 bytes of data (not all is necessarily valid, see Nbytes field)
  1412. ;
  1413. ; The buffer pool is all here, and as processes need buffer space, it is all
  1414. ; allocated out of this pool.  See allocate_buffer and free_buffer code.
  1415.  
  1416.  
  1417. ;;;---------------------------------------------------------------------------
  1418. ;;; return in HL a pointer to a free buffer.  If there are not more buffers,
  1419. ;;; return with Z flag set.
  1420. ;;; destroys no registers except return value HL.
  1421. ;;; IS CALLED FROM AN INTERRUPT ROUTINE, so this operation is atomic.
  1422.  
  1423. allocate_buffer:
  1424.  
  1425.     push    bc
  1426.     push    af
  1427.  
  1428.     ld    hl,(free)        ;;;get pointer to head of free list
  1429.     ld    a,h
  1430.     or    l
  1431.     jp    nz,OK_allocate_buffer    ;;; assure we're not off the end
  1432.  
  1433. ;get here if no more buffers.  Return Z set - do not disturb A.
  1434.     pop    af
  1435.     ld    b,a            ;;; tuck A away for a moment...
  1436.     xor    a            ;;; turn on Z bit
  1437.     ld    a,b            ;;; retreive original A
  1438.     pop    bc
  1439.     ret
  1440.  
  1441. OK_allocate_buffer:
  1442.  
  1443.     xor    a
  1444.     ld    c,(hl)            ;;;grab lo byte of next free buffer
  1445.     ld    (hl),a            ;;; clear it out
  1446.     inc    hl
  1447.     ld    b,(hl)            ;;; "ld bc,(hl)" now hi byte
  1448.     ld    (hl),a            ;;; clear it out, too
  1449.     ld    (free),bc        ;;; update with new free list pointer
  1450.  
  1451.     dec    hl            ;;; Now HL is at head of new buffer
  1452.  
  1453.     pop    af
  1454.     ld    b,a            ;;; tuck A away for a moment...
  1455.     ld    a,1
  1456.     or    a            ;;; Turn Z bit off (i.e., all OK)
  1457.     ld    a,b            ;;; retreive original A
  1458.  
  1459.     pop    bc
  1460.     ret
  1461.  
  1462. ;;;---------------------------------------------------------------------------
  1463. ;;; free_buffer gets passed a pointer (in HL) to a buffer to be freed.  The
  1464. ;;; buffer is placed on the head of the free list.  The nbytes & nread fields
  1465. ;;; are made 0 before placing on free list.
  1466. ;;; THIS ROUTINE IS CALLED AT INTERRUPT LEVEL, so results are atomic.
  1467. ;;; no registers are disturbed at all.  The FREE pointer is updated, however.
  1468. ;;; 159 T states [ 63.6 usec @ 2.5 MHz ]
  1469.  
  1470. free_buffer:
  1471.     push    af
  1472.     push    bc        ;;;we'll use these
  1473.     push    hl        ;;;this will be new head of free list
  1474.  
  1475.     ld    bc,(free)    ;;;get old free head
  1476.     ld    (hl),c        ;;;put on free chain, first low byte...
  1477.     inc    hl
  1478.     ld    (hl),b        ;;; ...now hi byte
  1479.     xor    a
  1480.     inc    hl
  1481.     ld    (hl),a        ;;; zero out nbytes field
  1482.     inc    hl
  1483.     ld    (hl),a        ;;; and the nread field of new head of free
  1484.  
  1485.     pop    hl        ;;;get new head of free list back
  1486.     ld    (free),hl    ;;;and save it in memory where it belongs
  1487.  
  1488.     pop    bc
  1489.     pop    af
  1490.     ret
  1491. ;;; --------------------------------------------------------------------------
  1492. ;;; putchar - HL contains pointer to buffer, A contains the character to put
  1493. ;;; into the buffer.  Upon return, char is put into this buffer if ther is
  1494. ;;; room, else another buffer is allocated and HL is updated to point to this
  1495. ;;; new buffer.  The new buffer is chained onto the old buffer in this case.
  1496. ;;; The calling routine is responsible for maintaing both the head of a
  1497. ;;; particular buffer chain (if it needs it), and the current buffer being
  1498. ;;; manipulated. THIS ROUTINE IS CALLED AT INTERRUPT LEVEL, so is atomic.  No
  1499. ;;; registers disturbed, except that HL may have a new value.
  1500. ;;; 211 T states [  84.4 usec @ 2.5 MHz ]    no new buffer required
  1501. ;;; 338 T states [ 135.2 usec @ 2.5 MHz ]    New buffer needed
  1502.  
  1503. putchar:
  1504.     push    bc
  1505.     push    ix
  1506.     push    af
  1507.     push    hl        ;;;do it this way for a reason...
  1508.  
  1509.     pop    ix        ;;;get buffer pointer into IX
  1510.     ld    a,(ix+2)    ;;;grab nbytes field
  1511.     cp    124        ;;;max of 124 chars in a buffer
  1512.     call    z,putc_need_new_buffer
  1513. ;;; if it takes this call, it returns with a new buffer, with HL pointing to
  1514. ;;; it (as well as IX), and with A reg set to 0.
  1515. ;;; else just plunk into buffer
  1516.     inc    (ix+2)        ;;;one more char will go into this buffer
  1517.     ld    c,a        ;;;get previous nbytes
  1518.     xor    a
  1519.     ld    b,a        ;;; bc <- nbytes, filled out to 16 bits
  1520.     add    ix,bc        ;;; update ix to point to where char goes
  1521.     pop    af        ;;; retreive the char we want to save
  1522.     ld    (ix+4),a    ;;; save it in this buffer
  1523.  
  1524.     pop    ix
  1525.     pop    bc
  1526.     ret            ;;;done for the moment
  1527.  
  1528. ;;; 127 T states [ 50.8 usec @ 2.5 MHz ] (really part of prev routine)
  1529. putc_need_new_buffer:        ;;;prev buffer filled, get a new one
  1530.     push    de        ;;; working registers
  1531.     push    hl        ;;; save current buffer pointer
  1532.     call    allocate_buffer    ;;; grab a new buffer, addr is in HL
  1533.     ex    de,hl        ;;; "ld de,hl" - get new addr into DE for now
  1534.     pop    hl
  1535.     ld    (hl),e        ;;; link new buffer onto chain, lo byte first
  1536.     inc    hl
  1537.     ld    (hl),d        ;;; now hi byte, chaining done
  1538.  
  1539.     ex    de,hl        ;;; update HL for orig. calling routine's use
  1540.     push    hl
  1541.     pop    ix        ;;; upper routine needs ix pointing to new buf
  1542.     xor    a        ;;; and A is nbytes in calling routine, make..
  1543.                 ;;; zero for a new buffer
  1544.     pop    de        ;;; done with this working register
  1545.     ret            ;;; all done here, let calling routine finish
  1546.  
  1547. ;;; --------------------------------------------------------------------------
  1548. ;;; getchar - grab a character from the buffer pointed at by HL, return in A.
  1549. ;;; if the "nread" field of this buf = "nbytes" then this buffer is exhausted,
  1550. ;;; so follow the chain on to the next buffer & release old buffer.  If the
  1551. ;;; next chain is 0, or if the nbytes field is >= nread field, then there are
  1552. ;;; no more bytes.  In this case, return with Z bit set; normally return with
  1553. ;;; Z bit reset (That is, non-zero) indicating a valid char is in A.  Note
  1554. ;;; that if we need to follow the chain to a new buffer, HL will be updated,
  1555. ;;; too, so that the calling routine needs to deal with this.
  1556. ;;;         no registers changed except AF and possibly HL.
  1557. ;;; CALLED AT INTERRUPT LEVEL, so operation is atomic.
  1558. ;;; 212 T states [  84.8 usec @ 2.5 MHz ]    No new buffer needed
  1559. ;;; 493 T states [ 197.2 usec @ 2.5 MHz ]    if following chain
  1560.  
  1561. getchar:
  1562.     push    ix        ;;; save because is working reg
  1563.     push    bc        ;;; working regs here
  1564.  
  1565.     push    hl
  1566.     pop    ix        ;;; ix points to this buffer
  1567.  
  1568.     ld    a,(ix+3)    ;;; grab Nread
  1569.     cp    (ix+2)        ;;; compare with Nbytes
  1570.     call    z,getc_new_buf    ;;; if they are same, this buffer is spent
  1571.  
  1572.     inc    (ix+3)        ;;; we are reading one more char, update Nread
  1573.     inc    a
  1574.     cp    (ix+2)
  1575.     jp    nz,getc_pluck_character    ;;; if not looking at last character
  1576.  
  1577. ;;; else, is the "next" pointer 0?
  1578.     push    hl
  1579.     ld    b,a        ;;; !!!!! SAVE  A   REG  !!!!!!! 4 Jan 87
  1580.     ld    a,(hl)
  1581.     inc    hl
  1582.     or    (hl)
  1583.     ld    a,b        ;;; !!!! Restore A Reg  (Gasp!)
  1584.     pop    hl
  1585.     jr    nz,getc_pluck_character
  1586.  
  1587. ;;; else next is 0 and we are on last char - flush it & quit
  1588.     call    free_buffer
  1589.     pop    bc
  1590.     pop    ix
  1591.     ret            ;;; note that Z bit is set (from above)
  1592.  
  1593. ;;; else we can just pluck a character out of this buffer
  1594. getc_pluck_character:
  1595.     dec    a        ;;; fix A from above mucking around...
  1596.  
  1597.     ld    c,a        ;;; get old Nread into BC
  1598.     ld    b,0        ;;; ditto
  1599.     add    ix,bc        ;;; fix buffer pointer
  1600.     ld    a,1
  1601.     or    a        ;;; make Z bit reset
  1602.     ld    a,(ix+4)    ;;; get the desired byte
  1603.  
  1604.     pop    bc
  1605.     pop    ix
  1606.     ret            ;;; all for this simple case
  1607.  
  1608. ;;; old buffer is spent, get new one (if any)
  1609.  
  1610. getc_new_buf:
  1611.     push    de        ;;; need this reg here
  1612.     ld    e,(hl)        ;;; get lo byte of Next pointer
  1613.     inc    hl
  1614.     ld    d,(hl)        ;;; hi byte of Next pointer (now all in DE)
  1615.     dec    hl        ;;; HL now back to point at spent buffer
  1616.     call    free_buffer    ;;; give the buffer back
  1617.  
  1618.     ex    de,hl        ;;; "ld hl,de" - follow chain
  1619.     push    hl
  1620.     pop    ix        ;;; init new IX (same as HL in this routine)
  1621.     xor    a        ;;; A holds Nread (needed above)
  1622.     pop    de        ;;; release DE from use by this excursion
  1623.     ret
  1624.  
  1625. ;;; --------------------------------------------------------------------------
  1626. ;;; free_chain - MUST be called from interrupt routine to guarantee
  1627. ;;; atomicity.  Takes buffer chain pointed at by HL and returns them to free
  1628. ;;; buffer list
  1629. ;;; 303 T states + (n_on_chain-1)*238 T states
  1630. ;;; [ 121.2 usec + (n_on_chain-1)*95.2 usec ]
  1631.  
  1632. free_chain:
  1633.     push    af
  1634.     push    de
  1635.     push    hl        ;;; we will muck with these
  1636.  
  1637. fc_0:
  1638.     ld    e,(hl)        ;;; get lo part of next buffer pointer
  1639.     inc    hl
  1640.     ld    d,(hl)        ;;; now hi part of next buffer pointer
  1641.     dec    hl
  1642.     call    free_buffer    ;;; release this buffer
  1643.     ld    a,d
  1644.     or    e
  1645.     jp    z,fc_9        ;;; if "next" address is 0, we are at end
  1646. ;;; else we've got more on this chain - deal with them.
  1647.     ex    de,hl        ;;; "ld hl,de" - HL points to "next"
  1648.     jp    fc_0
  1649.  
  1650. fc_9:
  1651.     pop    hl
  1652.     pop    de
  1653.     pop    af
  1654.     ret
  1655.  
  1656. ;;; --------------------------------------------------------------------------
  1657. ;;; out_queue_insert - Places the just-received buffer on the output queue.
  1658. ;;; The address of the RX buffer just received is in HL.
  1659. ;;; The output queue is a circular buffer.  The output routine keeps sending
  1660. ;;; out buffers until its out_head_cbuf pointer equals its out_tail_cbuf
  1661. ;;; pointer. The output routine never mucks with the out_tail_cbuf pointer;
  1662. ;;; similarly, this routine never changes the out_head_cbuf pointer.  So it
  1663. ;;; is possible to
  1664. ;;; insert new entries into the output circular buffer queue without
  1665. ;;; disturbing the entry which is being sent to the output port.
  1666.  
  1667. out_queue_insert:
  1668.     push    af
  1669.     push    de
  1670.     push    hl        ;;; use these
  1671.  
  1672.     ex    de,hl        ;;; "ld de,hl" - move buffer to link addr
  1673.     ld    hl,(out_tail_cbuf) ;;; Grab next free location 
  1674.     ld    (hl),e        ;;; set lo addr 1st
  1675.     inc    hl
  1676.     ld    (hl),d        ;;; now hi addr
  1677.     inc    hl        ;;; Now HL points to next free entry in...
  1678.     ld    de,out_bottom    ;;; ...circ buf, unless we're at end
  1679.     or    a        ;;; clear carry
  1680.     push    hl        ;;; (may be be needed address)
  1681.     sbc    hl,de
  1682.     pop    hl        ;;; get back what we think is good
  1683.     jp    nz,oqi_0
  1684.     
  1685.     ld    hl,out_top    ;;; get here if we're at end of circ buffer.
  1686. oqi_0:
  1687.     ld    (out_tail_cbuf),hl
  1688.     pop    hl
  1689.     pop    de
  1690.     pop    af        ;;; keep clean
  1691.     ret
  1692.  
  1693.  
  1694. ;;;---------------------------------------------------------------------------
  1695. ;;; TX_Queue_Insert - similar to Out_queue_insert, but with different queue.
  1696. ;;; Also, increments the byte TX_Outstanding (which counts the number of
  1697. ;;; frames ready to be dumped to the modem port).  This routine, like
  1698. ;;; out_queue_insert, does not need to worry about queue wrap-around because
  1699. ;;; there are more entries in each of these queues than there are buffers
  1700. ;;; available.  Yes, I know this is a hack, and wastes some RAM space, but it
  1701. ;;; means I don't have to check for overflows here.
  1702. ;;; The queue is circular, and sometimes I call it a "CBuf" - Circular Buffer
  1703.  
  1704. TX_Queue_Insert:
  1705.     push    af
  1706.     push    de
  1707.     push    hl
  1708.     ex    de,hl            ;;; "ld de,hl" - save chain head in DE
  1709.     ld    hl,(TX_Tail_CBuf)    ;;; Next free location in TX CBuf
  1710.     ld    (hl),e
  1711.     inc    hl
  1712.     ld    (hl),d            ;;; put this chain into TX Queue
  1713.     inc    hl            ;;; HL is next availble TX Queue ...
  1714.     ld    de,TX_Bottom        ;;; ... unless we are at bottom of ...
  1715.     or    a            ;;; ... the TX Queue
  1716.     push    hl
  1717.     sbc    hl,de
  1718.     pop    hl
  1719.     jp    nz,TQI_0        ;;; go there if not at buffer bottom
  1720.  
  1721.     ld    hl,TX_Top        ;;; else reload with top of queue val
  1722. TQI_0:
  1723.     ld    (TX_Tail_CBuf),hl    ;;; save next free queue slot
  1724.     ld    hl,TX_Outstanding
  1725.     inc    (hl)            ;;; +1 more frame outstanding now
  1726.     pop    hl
  1727.     pop    de
  1728.     pop    af
  1729.     ret
  1730.  
  1731. ;-----------------------------------------------------------------------------
  1732. ; Setup HL & TX_Chain_Head for transmission of next chain.
  1733.  
  1734. TXnext_CBuf:
  1735.     push    af
  1736.     push    de
  1737.     ld    hl,(TX_Head_CBuf)
  1738.     ld    e,(hl)
  1739.     inc    hl
  1740.     ld    d,(hl)            ; DE -> next chain to transmit
  1741.     inc    hl            ; HL MIGHT be next CBuf entry pointer
  1742.     push    de
  1743.     ld    de,TX_Bottom
  1744.     or    a            ;clear carry
  1745.     push    hl            ;save what might be correct value
  1746.     sbc    hl,de
  1747.     pop    hl
  1748.     pop    de
  1749.     jp    nz,TXn_1        ;go there if not at end of circ. buf
  1750.  
  1751.     ld    hl,TX_Top        ;else we wrap aroune
  1752. TXn_1:
  1753.     ld    (TX_Head_CBuf),hl    ;save next circ buf pointer in mem
  1754.     ex    de,hl            ;return ptr to next chain to TX in HL
  1755.     ld    (TX_Chain_Head),hl    ;TX RCA routine needs this
  1756.     pop    de
  1757.     pop    af
  1758.     ret
  1759.  
  1760.  
  1761. ;-----------------------------------------------------------------------------
  1762. STA_on:        ;Turn the STA LED on.  ASSUMES that interrupts are disabled!
  1763.     push    af
  1764.     ld    a,5
  1765.     out    (A_ctl),a        ;;; ready to write WR5
  1766.     ld    a,(A_WR5)        ;;; get memory copy
  1767.     and    NOT ALED        ;;; set DTR bit to 0 so LED goes on
  1768.     out    (A_ctl),a        ;;; Actually turn on STA LED now...
  1769.     ld    (A_WR5),a        ;;; update memory copy
  1770.     pop    af
  1771.     ret
  1772. ;-----------------------------------------------------------------------------
  1773. STA_off:    ;Turn the STA LED off.  ASSUMES that interrupts are disabled!
  1774.     push    af
  1775.     ld    a,5
  1776.     out    (A_ctl),a        ;;; ready to write WR5
  1777.     ld    a,(A_WR5)        ;;; get memory copy
  1778.     or    ALED            ;;; set DTR bit to 1 so LED goes off
  1779.     out    (A_ctl),a        ;;; Actually turn off STA LED now...
  1780.     ld    (A_WR5),a        ;;; update memory copy
  1781.     pop    af
  1782.     ret
  1783.  
  1784. ;These routines MUST be called with interrupts disabled!
  1785. ;-----------------------------------------------------------------------------
  1786. STA_flip:
  1787.     push    af
  1788.     push    bc
  1789.     in    a,(A_ctl)        ;;;assure we are talking to ch 0
  1790.     ld    a,5
  1791.     out    (A_ctl),a        ;;; ready to write WR5
  1792.     ld    a,(A_WR5)        ;;; get memory copy
  1793.     ld    b,a            ;;; save original for a moment...
  1794.     and    ALED            ;;; Check the STA LED bit
  1795.     ld    a,b            ;;; retreive original
  1796.     jp    z,STA_f0        ;;; bit is a 0, so LED is on, make off
  1797. ;else make it go on (because it is now off)
  1798.     and    NOT ALED        ;;; set DTR bit to 0 so LED goes on
  1799.     jp    STA_f1
  1800. STA_f0:
  1801.     or    ALED            ;;; set DTR bit to 1 so LED goes off
  1802. STA_f1:
  1803.     out    (A_ctl),a        ;;; Actually turn off STA LED now...
  1804.     ld    (A_WR5),a        ;;; update memory copy
  1805.     pop    bc
  1806.     pop    af
  1807.     ret
  1808.     
  1809. ;-----------------------------------------------------------------------------
  1810. CON_on:
  1811.     push    af
  1812.     ld    a,5
  1813.     out    (B_ctl),a
  1814.     ld    a,BLEDon
  1815.     ld    (B_WR5),a        ;;; save in mem for flip routine
  1816.     out    (B_ctl),a
  1817.     pop    af
  1818.     ret
  1819. ;-----------------------------------------------------------------------------
  1820. CON_off:
  1821.     push    af
  1822.     ld    a,5
  1823.     out    (B_ctl),a
  1824.     ld    a,BLEDoff
  1825.     ld    (B_WR5),a        ;;; save in mem for flip routine
  1826.     out    (B_ctl),a
  1827.     pop    af
  1828.     ret
  1829. ;-----------------------------------------------------------------------------
  1830. CON_flip:
  1831.     push    af
  1832.     push    bc
  1833.     in    a,(B_ctl)        ;;;assure we are talking to ch 0
  1834.     ld    a,5
  1835.     out    (B_ctl),a        ;;; ready to write WR5
  1836.     ld    a,(B_WR5)        ;;; get memory copy
  1837.     ld    b,a            ;;; save original for a moment...
  1838.     and    BLED            ;;; Check the CON LED bit
  1839.     ld    a,b            ;;; retreive original
  1840.     jp    z,CON_f0        ;;; bit is a 0, so LED is on, make off
  1841. ;else make it go on (because it is now off)
  1842.     and    NOT BLED        ;;; set DTR bit to 0 so LED goes on
  1843.     jp    CON_f1
  1844. CON_f0:
  1845.     or    BLED            ;;; set DTR bit to 1 so LED goes off
  1846. CON_f1:
  1847.     out    (B_ctl),a        ;;; Actually turn off CON LED now...
  1848.     ld    (B_WR5),a        ;;; update memory copy
  1849.     pop    bc
  1850.     pop    af
  1851.     ret
  1852.     
  1853.  
  1854.  
  1855.     if    ROM
  1856.  
  1857. Free_RAM    equ    8000h
  1858.  
  1859.     else
  1860.  
  1861. Free_RAM    equ    $
  1862.  
  1863.     endif;    ROM
  1864.  
  1865.  
  1866. ;-----------------------------------------------------------------------------
  1867. ; These are the TX real-time routine data structures.  They are used for
  1868. ; timing required with TX control.  There are 3 actions that must be timed:
  1869. ; 1) TXR_delay        TX Delay Timer (for TXDELAY function)
  1870. ; 2) TXR_SlotTime    Part of p-persistence backoff
  1871. ; 3) TXR_tail        Timer to be sending SYNCs before dropping RTS
  1872.  
  1873. ; The data structure can be thought of logically as this:
  1874. ;
  1875. ;    +------------------------+
  1876. ;    | Routine Enabled (byte) | is 0 if not enabled, non zero if enabled
  1877. ;    +------------------------+--------------------------------------+
  1878. ;    | Pointer to routine to execute when timer expires (word)    |
  1879. ;    +---------------------------------------------------------------+
  1880. ;    | 16-bit downcounter timer value, in 10s of milliseconds (word)    |
  1881. ;    +---------------------------------------------------------------+
  1882. ;
  1883. ; The data structure has one entry for each of the 3 timer events.  Physically
  1884. ; it is organized as 3 separate lists, one for each of the enables, one for
  1885. ; each of the routine pointers, and one for each of the timer values.
  1886. ;
  1887. ; An interupt routine, running at 10 millisecond ticks, decrements the values
  1888. ; in each of the downcount timer whether a routine is enabled or not.  When
  1889. ; downcount value goes to 0 (or negative) then the routine "fires".  This
  1890. ; checking for "firing" happens at non-interrupt level in the commutator loop.
  1891. ; With this scheme, the minimum time before firing is 10 milliseconds, and the
  1892. ; maximum time is 327.67 seconds (over 5 minutes).  For example, for a
  1893. ; TXDELAY of 600 milliseconds, the timer would get loaded with decimal 60.
  1894. ;
  1895. ; When a routine fires, it gets marked as "disabled", so you'd need to
  1896. ; explicitly re-enable it if this is required
  1897.  
  1898. ; Note too that a clock could be easily implemented.  If we inserted another
  1899. ; event into our list with a timeout of 100, then every second a routine would
  1900. ; be called. In that routine, we could increment the seconds field (and
  1901. ; possibly minutes, hours, days, years fields) of a Time-of-Day clock.  We
  1902. ; would immediately re-activate this timer to get the next tick, etc.
  1903.  
  1904.  
  1905. TXQ_Enables    equ    Free_RAM
  1906.         ;ds    4        ; 4 bytes for the enables
  1907.  
  1908. TXQ_Addresses    equ    TXQ_Enables+4
  1909.         ;ds    8        ; 4 words for the routine pointers
  1910.  
  1911. TXQ_Timers    equ    TXQ_Addresses+8
  1912.         ;ds    8        ; 4 words for the routine timers
  1913.  
  1914. ; NOTE the last slot in this table is for R_Test routine, which blinks STA LED
  1915. ; IT IS NOT USED NORMALLY, JUST FOR HELPING ME DEBUG THIS!
  1916.  
  1917. ; Some equates to save us from doing contorted things when we want to check if
  1918. ; a routine is enabled in places other than the commutator loop, or for
  1919. ; enabling routines, etc.
  1920.  
  1921. TXQE_Delay    equ    TXQ_Enables+0
  1922. TXQE_SlotTime    equ    TXQ_Enables+1
  1923. TXQE_Tail    equ    TXQ_Enables+2
  1924.  
  1925. ; Same idea, but for the timer values
  1926.  
  1927. TXQT_Delay    equ    TXQ_Timers+0
  1928. TXQT_SlotTime    equ    TXQ_Timers+2
  1929. TXQT_Tail    equ    TXQ_Timers+4
  1930.  
  1931. ; We don't do this for the routine addresses, since they don't change once
  1932. ; they are initialized.
  1933.  
  1934.  
  1935. TXdelay        equ    TXQ_Timers+8
  1936.         ;ds    1        ; Transmitter Delay time value
  1937.  
  1938. Persistence    equ    TXdelay+1
  1939.         ;ds    1        ; Persistence value
  1940.  
  1941. SlotTime    equ    Persistence+1
  1942.         ;ds    1        ; Slot Time value
  1943.  
  1944. TailTime    equ    SlotTime+1
  1945.         ;ds    1        ; TX Tail Time value
  1946.  
  1947.  
  1948. nbuffers    equ    TailTime+1
  1949.         ;db    0        ;up to 255 buffers
  1950.  
  1951. free        equ    nbuffers+1
  1952.         ;dw    0        ;address of 1st buffer on free list
  1953.  
  1954.  
  1955. RX_buf        equ    free+2
  1956.         ;dw    0        ;address of current Receive buffer
  1957.  
  1958. RX_head        equ    RX_buf+2
  1959.         ;dw    0        ;address of 1st RX buffer
  1960.  
  1961. RX_Allocated_Buffer    equ    RX_head+2
  1962.         ;db    0        ;set non-zero if we're in RX state
  1963.  
  1964. RX_Flushing    equ    RX_Allocated_Buffer+1
  1965.         ;db    0        ;is non-0 if we ran out of buffer
  1966.                     ;space and are currently flushing this
  1967.                     ;frame being received.  Used by
  1968.                     ;ia_rca and reset by ia_ext.
  1969.  
  1970.  
  1971. In_Buffer    equ    RX_Flushing+1
  1972.         ;dw    0        ;addr of current Input buffer
  1973.  
  1974. In_Head        equ    In_Buffer+2
  1975.         ;dw    0        ;addr of 1st Input Buffer
  1976.  
  1977. In_Allocated_Buffer    equ    In_Head+2
  1978.         ;db    0        ;is not 0 if we've already alloc'd buf
  1979.  
  1980. In_State    equ    In_Allocated_Buffer+1
  1981.         ;db    1        ;input state machine state
  1982.                     ;assume that we've seen an FEND from
  1983.                     ;(non-existent) "previous" frame. This
  1984.                     ;means that when we are receiving data
  1985.                     ;from user, there need be ONLY the
  1986.                     ;FEND char at the end of a frame, and
  1987.                     ;not at the beginning (although if a
  1988.                     ;FEND is at the beginning, it is 
  1989.                     ;ignored.)
  1990.  
  1991. Out_Started    equ    In_State+1
  1992.         ;db    0        ;Output not started yet (Logical var)
  1993.  
  1994. Out_Head_CBuf    equ    Out_Started+1
  1995.         ;dw    out_top        ;address of buffer to be output rs232
  1996.  
  1997. Out_Tail_CBuf    equ    Out_Head_Cbuf+2
  1998.         ;dw    out_top        ;pointer to next free output buffer
  1999.  
  2000. Out_Chain_Head    equ    Out_Tail_Cbuf+2
  2001.         ;dw    0        ;addr of buffer we are now outputting
  2002.  
  2003.  
  2004. TX_Started    equ    Out_Chain_Head+2
  2005.         ;db    0        ;non-zero if we've begun TXing chars
  2006.  
  2007. TX_Head_CBuf    equ    TX_Started+1
  2008.         ;dw    TX_Top        ;Current active CBuf entry (if active)
  2009.  
  2010. TX_Tail_CBuf    equ    TX_Head_CBuf+2    ; This said "TX_Head_CBuf_2"...sigh
  2011.                     ;type found 2 Mar 87
  2012.  
  2013.         ;dw    TX_Top        ;next free CBuf entry
  2014.  
  2015.  
  2016.  
  2017. TX_Chain_Head    equ    TX_Tail_Cbuf+2
  2018.         ;dw    0        ;holds address of the current buffer
  2019.                     ;chain head that we are transmitting
  2020.  
  2021. TX_Outstanding    equ    TX_Chain_Head+2
  2022.         ;db    0        ;Number of TX CBufs queued up for TX
  2023.  
  2024.  
  2025. DCD_State    equ    TX_Outstanding+1
  2026.         ;db    0        ;is non 0 if DCD LED is on
  2027.  
  2028.  
  2029. ;these next two are used by the IB_TBE interrupt routine.
  2030. ib_esc_mode    equ    DCD_State+1
  2031.         ;db    0        ; not in escaped mode 
  2032.  
  2033. ib_char        equ    ib_esc_mode+1
  2034.         ;ds    1        ; next char to send if escaped mode
  2035.  
  2036. in_break    equ    ib_char+1    ; non-zero if we are in a break detect
  2037.         ;db    0        ; on the async port
  2038.  
  2039. Full_Duplex    equ    in_break+1
  2040.         ;db    0        ;not initially Full Duplex
  2041.  
  2042. A_WR5        equ    Full_Duplex+1
  2043.         ;db    ALEDoff        ;state of STA LED & RTS (PTT) line,
  2044.                     ;mainly... (For Ch A only [modem] )
  2045.  
  2046. B_WR5        equ    A_WR5+1
  2047.         ;db    BLEDoff
  2048.  
  2049.  
  2050.  
  2051. Out_Top        equ    B_WR5+2        ;"top" of output circular buffer
  2052.                     ; 255 out buffer chains pending, max
  2053. Out_Bottom    equ    Out_Top+2*255    ;"bottom" of output circular buffer
  2054.  
  2055. TX_Top        equ    Out_Bottom+2
  2056. TX_Bottom    equ    TX_Top+2*255
  2057.  
  2058.  
  2059. Bottom        equ    TX_Bottom+2    ;end of all code & predefined data
  2060.  
  2061.  
  2062. ; Notes on nomenclature:
  2063.  
  2064. ;    out = to TTY port; in = from TTY port
  2065. ;    TX = to modem; RX = from modem
  2066. ;
  2067. ;    ;;; means that that code executes without interrupts enabled (except
  2068. ;        for the initialization code)
  2069. ;
  2070. ;
  2071. ; I have been careful with JR/JP use.  I use JP when the jump is likely and
  2072. ; where speed is important.  I use JR when the jump is unlikely so that I can
  2073. ; save a few cycles.  JP always uses 10 cycles whether it jumps or not, but
  2074. ; JR uses either 7 or 12 T states, no jump/jump, respectively.
  2075.  
  2076.  
  2077. ; Buffers kept here at end.
  2078.     end    start
  2079.